/*
 * event_hub.c
 *
 * event_hub routines
 *
 * Copyright (C) 2020 Long-Horn
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h> 
#include <sys/time.h> 
#include <fcntl.h>
#include <limits.h>
#include <string.h> 
#include <errno.h>
#include <pthread.h>

#include "event_hub.h"

static int  event_hub_start(void *event_hub)
{
    event_hub_s * pevent_hub = (event_hub_s *)event_hub;
    
    pevent_hub->mcu_proxy->start(pevent_hub->mcu_proxy);
    
    pthread_mutex_lock(&pevent_hub->mutex);
    pevent_hub->running = 1;
    pthread_cond_signal(&pevent_hub->startable);
    pthread_mutex_unlock(&pevent_hub->mutex);
   
    return 0;
}

static int event_hub_stop(void *event_hub)
{
    event_hub_s * pevent_hub = (event_hub_s *)event_hub;
    
    pevent_hub->mcu_proxy->stop(pevent_hub->mcu_proxy);
    
    pevent_hub->running = 0;
    
    return 0;
}

static int send_event_to_proxy(void *event_hub, event_s *event)
{
    int res;
    
    event_hub_s * pevent_hub = (event_hub_s *)event_hub;
#if 0
    if(event->to) { // to avm proxy
        res = pevent_hub->avm_proxy->send_event(pevent_hub->avm_proxy, event);
    } else { // to mcu proxy
        res = pevent_hub->mcu_proxy->send_event(pevent_hub->mcu_proxy, event);
    }
#endif

    res = pevent_hub->mcu_proxy->send_event(pevent_hub->mcu_proxy, event);
    
    return res;
}

static int event_hub_send_event(void *event_hub, event_s *event)
{
    int res;
    
    res = send_event_to_proxy(event_hub, event);
    
    return res;
}

static int event_hub_send_event_with_response(void *event_hub, event_s *event)
{
    event_hub_s * pevent_hub = (event_hub_s *)event_hub;
    
    return 0;
}

static int get_event_from_queue(void *event_hub, event_s *event)
{
    int res;
    event_hub_s * pevent_hub = (event_hub_s *)event_hub;
    
    res = pevent_hub->event_q->dequeue(pevent_hub->event_q, event);
    
    return res;
}

static void dispatch_event_to_listener(void *event_hub, event_s *event)
{
    event_hub_s * pevent_hub = (event_hub_s *)event_hub;

    event_callback_s *event_cb;
    node_s *node, *tmp;

    list_for_each(pevent_hub->listener, node, tmp) {
        if (NULL != node) {
            event_cb = (event_callback_s *)node->data;
            event_cb->callback((void *)event);
        }
    }
}

/* Main routine for the dispatch thread */
void * event_hub_dispatch_handle(void *data)
{
    event_s event;

    int res;
    event_hub_s *pevent_hub = (event_hub_s *)data;

    printf("%s: enter!\n", __func__);

    pthread_mutex_lock(&pevent_hub->mutex);
    while ( 0 == pevent_hub->running) {
        pthread_cond_wait(&pevent_hub->startable, &pevent_hub->mutex);
    }
    pthread_mutex_unlock(&pevent_hub->mutex);

    printf("event_hub_receive_handle thread started\n");

    while (pevent_hub->running) {
        res = get_event_from_queue(pevent_hub, &event);
        if (res <= 0) {
            fprintf(stderr, "%s : get_event_from_queue error \n", __func__);
        } else {
            dispatch_event_to_listener(pevent_hub, &event);
        }
    }

    fprintf(stderr, "%s exiting\n", __func__);

    pthread_exit((void *)0);

    return (void *)0;
}

int create_event_hub(event_hub_s **event_hub)
{
    int res = -1;
    
    event_hub_s *pevent_hub = NULL;
    pevent_hub = (event_hub_s *)malloc(sizeof(event_hub_s));
    if(NULL == pevent_hub) {
        printf("### %s: malloc event_hub_s failed!\n", __func__);
        return res;
    }
    
    res = create_event_queue(&pevent_hub->event_q, 128);
    if (res < 0) {
        printf("### %s: malloc create_event_queue  128 failed!\n", __func__);
        free(pevent_hub);
        return res;
    }
    
    pevent_hub->listener = list_new("event_cb_list");
    if (NULL == pevent_hub->listener) {
        destroy_event_queue(pevent_hub->event_q);
        free(pevent_hub);
        return res;
    }
    
    pevent_hub->running = 0;
    pevent_hub->start = event_hub_start;
    pevent_hub->stop = event_hub_stop;
    pevent_hub->send_event = event_hub_send_event;
    pevent_hub->send_event_with_response = event_hub_send_event_with_response;
    *event_hub = pevent_hub;
    
    if (0 != pthread_mutex_init(&pevent_hub->mutex, NULL)) {
        destroy_event_queue(pevent_hub->event_q);
        free(pevent_hub->listener);
        free(pevent_hub);
        return res;
    }
    if (0 != pthread_cond_init(&pevent_hub->startable, NULL)) {
        destroy_event_queue(pevent_hub->event_q);
        free(pevent_hub->listener);
        free(pevent_hub);
        return res;
    }
    
    pthread_attr_t attr;
    pthread_t thread;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    res = pthread_create(&thread, &attr, event_hub_dispatch_handle, (void *)pevent_hub);
    pthread_attr_destroy(&attr);
    
    res = create_mcu_event_proxy(&pevent_hub->mcu_proxy, pevent_hub->event_q, 0, 2000);
    if (res < 0) {
        printf("### %s: malloc create_mcu_event_proxy  failed!\n", __func__);
        destroy_event_queue(pevent_hub->event_q);
        free(pevent_hub->listener);
        free(pevent_hub);
        return res;    
    }
    
    printf("%s: OK!\n", __func__);
	
    return res;
}

void destroy_event_hub(event_hub_s *event_hub)
{
    destroy_mcu_event_proxy(event_hub->mcu_proxy);
    
    free(event_hub);
    
    return;
}

void event_hub_register_callback(void *event_hub, event_callback_s *event_callback)
{
    event_hub_s * pevent_hub = (event_hub_s *)event_hub;
    
    list_add(pevent_hub->listener, (void *)event_callback);
}
